import numpy as np
import copy

def demonstrate_numpy_copy_view():
    """
    演示NumPy副本和视图的区别
    基于菜鸟教程的完整知识点实现
    """
    
    print("=" * 60)
    print("NumPy 副本和视图演示")
    print("=" * 60)
    
    # 1. 无复制（简单赋值）
    print("\n1. 无复制 - 简单赋值")
    print("-" * 40)
    
    a = np.arange(6)
    print("原始数组 a:")
    print(a)
    print(f"a 的 id(): {id(a)}")
    
    # 简单赋值 - 不创建副本，只是引用
    b = a
    print("\n简单赋值 b = a 后:")
    print(f"b 的 id(): {id(b)}")
    print(f"a 和 b 的 id 是否相同: {id(a) == id(b)}")
    
    # 修改 b 的形状
    b.shape = (3, 2)
    print("\n修改 b 的形状为 (3, 2):")
    print("b 的形状:")
    print(b)
    print("a 的形状:")
    print(a)
    print("注意：a 的形状也随之改变！")
    
    # 2. 视图（浅拷贝）
    print("\n2. 视图（浅拷贝）")
    print("-" * 40)
    
    # 重置数组
    a = np.arange(6).reshape(3, 2)
    print("重新创建数组 a:")
    print(a)
    print(f"a 的 id(): {id(a)}")
    
    # 使用 view() 创建视图
    b = a.view()
    print("\n使用 a.view() 创建视图 b:")
    print("b:")
    print(b)
    print(f"b 的 id(): {id(b)}")
    print(f"a 和 b 的 id 是否相同: {id(a) == id(b)}")
    
    # 修改视图的形状
    b.shape = (2, 3)
    print("\n修改视图 b 的形状为 (2, 3):")
    print("b 的形状:")
    print(b)
    print("a 的形状:")
    print(a)
    print("注意：a 的形状保持不变！")
    
    # 修改视图的数据内容
    b[0, 0] = 999
    print("\n修改视图 b 的数据 b[0,0] = 999:")
    print("修改后的 b:")
    print(b)
    print("修改后的 a:")
    print(a)
    print("注意：a 的数据内容也随之改变！")
    
    # 3. 切片创建的视图
    print("\n3. 切片创建的视图")
    print("-" * 40)
    
    arr = np.arange(12)
    print("原始数组 arr:")
    print(arr)
    
    # 创建切片视图
    a = arr[3:]
    b = arr[3:]
    print(f"\n创建切片 a = arr[3:], id: {id(a)}")
    print(f"创建切片 b = arr[3:], id: {id(b)}")
    print(f"arr[3:] 的临时 id: {id(arr[3:])}")
    print("注意：每次切片都会创建新的视图对象")
    
    # 修改切片视图
    a[1] = 123
    b[2] = 234
    print("\n修改切片视图:")
    print("a[1] = 123, b[2] = 234")
    print("修改后的 arr:")
    print(arr)
    print("注意：通过切片视图修改会影响原始数组！")
    
    # 4. 副本（深拷贝）
    print("\n4. 副本（深拷贝）")
    print("-" * 40)
    
    a = np.array([[10, 10], [2, 3], [4, 5]])
    print("原始数组 a:")
    print(a)
    print(f"a 的 id(): {id(a)}")
    
    # 使用 copy() 创建副本
    b = a.copy()
    print("\n使用 a.copy() 创建副本 b:")
    print("b:")
    print(b)
    print(f"b 的 id(): {id(b)}")
    print(f"a 和 b 的 id 是否相同: {id(a) == id(b)}")
    print(f"b 是 a 吗: {b is a}")
    
    # 修改副本
    b[0, 0] = 100
    print("\n修改副本 b[0,0] = 100:")
    print("修改后的 b:")
    print(b)
    print("修改后的 a:")
    print(a)
    print("注意：a 保持不变！")
    
    # 5. Python 的 copy 模块对比
    print("\n5. Python copy 模块对比")
    print("-" * 40)
    
    a = np.array([[1, 2], [3, 4]])
    print("原始数组 a:")
    print(a)
    
    # 浅拷贝
    b_shallow = copy.copy(a)
    # 深拷贝
    b_deep = copy.deepcopy(a)
    
    print("\n使用 copy.copy() (浅拷贝):")
    b_shallow[0, 0] = 999
    print("修改浅拷贝后:")
    print("b_shallow:", b_shallow)
    print("a:", a)
    
    print("\n使用 copy.deepcopy() (深拷贝):")
    b_deep[0, 0] = 888
    print("修改深拷贝后:")
    print("b_deep:", b_deep)
    print("a:", a)
    
    # 6. 高级示例：嵌套数组的拷贝
    print("\n6. 嵌套数组的拷贝")
    print("-" * 40)
    
    # 创建包含列表的数组
    a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], dtype=object)
    print("复杂数组 a:")
    print(a)
    
    # 视图
    b_view = a.view()
    # 副本
    b_copy = a.copy()
    
    # 修改内部列表
    a[0, 0][0] = 999
    print("\n修改原始数组的内部元素后:")
    print("a:", a)
    print("b_view (视图):", b_view)
    print("b_copy (副本):", b_copy)
    print("注意：对于对象数组，copy() 可能不是真正的深拷贝！")

def memory_address_demo():
    """
    内存地址演示，更直观展示副本和视图的区别
    """
    print("\n" + "=" * 60)
    print("内存地址对比演示")
    print("=" * 60)
    
    a = np.array([1, 2, 3, 4, 5])
    print(f"原始数组 a: {a}")
    print(f"a 的内存地址: {hex(id(a))}")
    print(f"a 的数据缓冲区地址: {hex(a.ctypes.data)}")
    
    # 简单赋值
    b = a
    print(f"\n简单赋值 b = a")
    print(f"b 的内存地址: {hex(id(b))}")
    print(f"b 的数据缓冲区地址: {hex(b.ctypes.data)}")
    print(f"数据缓冲区是否相同: {a.ctypes.data == b.ctypes.data}")
    
    # 视图
    c = a.view()
    print(f"\n视图 c = a.view()")
    print(f"c 的内存地址: {hex(id(c))}")
    print(f"c 的数据缓冲区地址: {hex(c.ctypes.data)}")
    print(f"数据缓冲区是否相同: {a.ctypes.data == c.ctypes.data}")
    
    # 副本
    d = a.copy()
    print(f"\n副本 d = a.copy()")
    print(f"d 的内存地址: {hex(id(d))}")
    print(f"d 的数据缓冲区地址: {hex(d.ctypes.data)}")
    print(f"数据缓冲区是否相同: {a.ctypes.data == d.ctypes.data}")

def practical_use_cases():
    """
    实际应用场景演示
    """
    print("\n" + "=" * 60)
    print("实际应用场景")
    print("=" * 60)
    
    # 场景1：数据处理时不希望影响原始数据
    print("场景1：保护原始数据")
    original_data = np.random.rand(5, 5)
    processed_data = original_data.copy()  # 使用副本进行处理
    
    # 对处理数据进行操作
    processed_data[processed_data < 0.5] = 0
    print("原始数据（保持不变）:")
    print(original_data)
    print("处理后的数据:")
    print(processed_data)
    
    # 场景2：多个视图操作同一数据
    print("\n场景2：多视图协同操作")
    shared_data = np.zeros(10)
    view1 = shared_data[0:5]    # 前5个元素的视图
    view2 = shared_data[5:10]   # 后5个元素的视图
    
    view1[:] = 1    # 通过视图1修改
    view2[:] = 2    # 通过视图2修改
    
    print("通过不同视图修改后的共享数据:")
    print(shared_data)

if __name__ == "__main__":
    # 执行主要演示
    demonstrate_numpy_copy_view()
    
    # 内存地址演示
    memory_address_demo()
    
    # 实际应用场景
    practical_use_cases()
    
    # 总结说明
    print("\n" + "=" * 70)
    print("知识点总结:")
    print("=" * 70)
    print("""
重要概念总结：

1. 无复制（简单赋值）:
   - b = a
   - 共享相同的内存地址和数据缓冲区
   - 修改任何一个都会影响另一个

2. 视图（浅拷贝）:
   - b = a.view() 或 b = a[:]
   - 创建新的数组对象，但共享相同的数据缓冲区
   - 可以有不同的形状和步长，但数据是共享的
   - 修改数据内容会影响原始数组

3. 副本（深拷贝）:
   - b = a.copy()
   - 创建完全独立的新数组，包括新的数据缓冲区
   - 修改副本不会影响原始数组

4. 关键区别:
   - 内存效率: 视图 > 无复制 > 副本
   - 数据安全: 副本 > 视图 > 无复制
   - 性能: 无复制 > 视图 > 副本

5. 使用建议:
   - 需要保护原始数据时使用 copy()
   - 需要高效处理大数据时使用 view()
   - 明确知道需要共享数据时才使用简单赋值
    """)